var responseDisplay = 'data';
var coreapi = window.coreapi;
var schema = window.schema;

function normalizeKeys(arr) {
    var _normarr = [];
    for (var i = 0; i < arr.length; i++) {
        _normarr = _normarr.concat(arr[i].split(' > '));
    }
    return _normarr;
}

function normalizeHTTPHeader(str) {
    // Capitalize HTTP headers for display.
    return (str.charAt(0).toUpperCase() + str.substring(1))
        .replace(/-(.)/g, function ($1) {
            return $1.toUpperCase()
        })
        .replace(/(Www)/g, function ($1) {
            return 'WWW'
        })
        .replace(/(Xss)/g, function ($1) {
            return 'XSS'
        })
        .replace(/(Md5)/g, function ($1) {
            return 'MD5'
        })
}

function formEntries(form) {
    // Polyfill for new FormData(form).entries()
    var formData = new FormData(form);
    if (formData.entries !== undefined) {
        return Array.from(formData.entries())
    }

    var entries = [];

    for (var i = 0; i < form.elements.length; i++) {
        var element = form.elements[i];

        if (!element.name) {
            continue
        }

        if (element.type === 'file') {
            for (var j = 0; j < element.files.length; j++) {
                entries.push([element.name, element.files[j]])
            }
        } else if (element.type === 'select-multiple' || element.type === 'select-one') {
            for (var j = 0; j < element.selectedOptions.length; j++) {
                entries.push([element.name, element.selectedOptions[j].value])
            }
        } else if (element.type === 'checkbox') {
            if (element.checked) {
                entries.push([element.name, element.value])
            }
        } else {
            entries.push([element.name, element.value])
        }
    }

    return entries
}

$(function () {
    var $selectedAuthentication = $('#selected-authentication');
    var $authControl = $('#auth-control');
    var $authTokenModal = $('#auth_token_modal');
    var $authBasicModal = $('#auth_basic_modal');
    var $authSessionModal = $('#auth_session_modal');

    // Language Control
    $('#language-control li').click(function (event) {
        event.preventDefault();
        var $languageMenuItem = $(this).find('a');
        var $languageControls = $(this).closest('ul').find('li');
        var $languageControlLinks = $languageControls.find('a');
        var language = $languageMenuItem.data('language');

        $languageControlLinks.not('[data-language="' + language + '"]').parent().removeClass('active');
        $languageControlLinks.filter('[data-language="' + language + '"]').parent().addClass('active');

        $('#selected-language').text(language);

        var $codeBlocks = $('pre.highlight');
        $codeBlocks.not('[data-language="' + language + '"]').addClass('hide');
        $codeBlocks.filter('[data-language="' + language + '"]').removeClass('hide')
    });

    // API Explorer
    $('form.api-interaction').submit(function (event) {
        event.preventDefault();

        var $form = $(this).closest('form');
        var $requestMethod = $form.find('.request-method');
        var $requestUrl = $form.find('.request-url');
        var $toggleView = $form.closest('.modal-content').find('.toggle-view');
        var $responseStatusCode = $form.find('.response-status-code');
        var $meta = $form.find('.meta');
        var $responseRawResponse = $form.find('.response-raw-response');
        var $requestAwaiting = $form.find('.request-awaiting');
        var $responseRaw = $form.find('.response-raw');
        var $responseData = $form.find('.response-data');
        var key = normalizeKeys($form.data('key'));
        var params = {};
        var entries = formEntries($form.get()[0]);

        for (var i = 0; i < entries.length; i++) {
            var entry = entries[i];
            var paramKey = entry[0];
            var paramValue = entry[1];
            var $elem = $form.find('[name="' + paramKey + '"]');
            var dataType = $elem.data('type') || 'string';

            if (dataType === 'integer' && paramValue) {
                var value = parseInt(paramValue);
                if (!isNaN(value)) {
                    params[paramKey] = value
                }
            } else if (dataType === 'number' && paramValue) {
                var value = parseFloat(paramValue);
                if (!isNaN(value)) {
                    params[paramKey] = value
                }
            } else if (dataType === 'boolean' && paramValue) {
                var value = {
                    'true': true,
                    'false': false
                }[paramValue.toLowerCase()];
                if (value !== undefined) {
                    params[paramKey] = value
                }
            } else if (dataType === 'array' && paramValue) {
                try {
                    params[paramKey] = JSON.parse(paramValue)
                } catch (err) {
                    // Ignore malformed JSON
                }
            } else if (dataType === 'object' && paramValue) {
                try {
                    params[paramKey] = JSON.parse(paramValue)
                } catch (err) {
                    // Ignore malformed JSON
                }
            } else if (dataType === 'string' && paramValue) {
                params[paramKey] = paramValue
            }
        }

        $form.find(':checkbox').each(function (index) {
            // Handle unselected checkboxes
            var name = $(this).attr('name');
            if (!params.hasOwnProperty(name)) {
                params[name] = false
            }
        });

        function requestCallback(request) {
            // Fill in the "GET /foo/" display.
            var parser = document.createElement('a');
            parser.href = request.url;
            var method = request.options.method;
            var path = parser.pathname + parser.hash + parser.search;

            $requestMethod.text(method);
            $requestUrl.text(path)
        }

        function responseCallback(response, responseText) {
            // Display the 'Data'/'Raw' control.
            $toggleView.removeClass('hide');

            // Fill in the "200 OK" display.
            $responseStatusCode.removeClass('label-success').removeClass('label-danger');
            if (response.ok) {
                $responseStatusCode.addClass('label-success')
            } else {
                $responseStatusCode.addClass('label-danger')
            }
            $responseStatusCode.text(response.status);
            $meta.removeClass('hide');

            // Fill in the Raw HTTP response display.
            var panelText = 'HTTP/1.1 ' + response.status + ' ' + response.statusText + '\n';
            response.headers.forEach(function (header, key) {
                panelText += normalizeHTTPHeader(key) + ': ' + header + '\n'
            });
            if (responseText) {
                panelText += '\n' + responseText
            }
            $responseRawResponse.text(panelText)
        }

        // Instantiate a client to make the outgoing request.
        var options = {
            requestCallback: requestCallback,
            responseCallback: responseCallback
        };

        // Setup authentication options.
        if (window.auth && window.auth.type === 'token') {
            // Header authentication
            options.auth = new coreapi.auth.TokenAuthentication({
                scheme: window.auth.scheme,
                token: window.auth.token
            })
        } else if (window.auth && window.auth.type === 'basic') {
            // Basic authentication
            options.auth = new coreapi.auth.BasicAuthentication({
                username: window.auth.username,
                password: window.auth.password
            })
        } else if (window.auth && window.auth.type === 'session') {
            // Session authentication
            options.auth = new coreapi.auth.SessionAuthentication({
                csrfCookieName: 'csrftoken',
                csrfHeaderName: 'X-CSRFToken'
            })
        }

        var client = new coreapi.Client(options);
        client.action(schema, key, params).then(function (data) {
            var response = JSON.stringify(data, null, 2);
            $requestAwaiting.addClass('hide');
            $responseRaw.addClass('hide');
            $responseData.addClass('hide').text('').jsonView(response);

            if (responseDisplay === 'data') {
                $responseData.removeClass('hide')
            } else {
                $responseRaw.removeClass('hide')
            }
        }).catch(function (error) {
            var response = JSON.stringify(error.content, null, 2);
            $requestAwaiting.addClass('hide');
            $responseRaw.addClass('hide');
            $responseData.addClass('hide').text('').jsonView(response);

            if (responseDisplay === 'data') {
                $responseData.removeClass('hide')
            } else {
                $responseRaw.removeClass('hide')
            }
        })
    });

    // 'Data'/'Raw' control
    $('.toggle-view button').click(function () {
        var $modalContent = $(this).closest('.modal-content');
        var $modalResponseRaw = $modalContent.find('.response-raw');
        var $modalResponseData = $modalContent.find('.response-data');

        responseDisplay = $(this).data('display-toggle');

        $(this).removeClass('btn-default').addClass('btn-info').siblings().removeClass('btn-info');

        if (responseDisplay === 'raw') {
            $modalResponseRaw.removeClass('hide');
            $modalResponseData.addClass('hide')
        } else {
            $modalResponseData.removeClass('hide');
            $modalResponseRaw.addClass('hide')
        }
    });

    // Authentication: none
    $authControl.find("[data-auth='none']").click(function (event) {
        event.preventDefault();
        window.auth = null;
        $selectedAuthentication.text('none');
        $authControl.find("[data-auth]").closest('li').removeClass('active');
        $authControl.find("[data-auth='none']").closest('li').addClass('active')
    });

    // Authentication: token
    $('form.authentication-token-form').submit(function (event) {
        event.preventDefault();
        var $form = $(this).closest('form');
        var scheme = $form.find('input#scheme').val();
        var token = $form.find('input#token').val();
        window.auth = {
            'type': 'token',
            'scheme': scheme,
            'token': token
        };
        $selectedAuthentication.text('token');
        $authControl.find("[data-auth]").closest('li').removeClass('active');
        $authControl.find("[data-auth='token']").closest('li').addClass('active');
        $authTokenModal.modal('hide')
    });

    // Authentication: basic
    $('form.authentication-basic-form').submit(function (event) {
        event.preventDefault();
        var $form = $(this).closest('form');
        var username = $form.find('input#username').val();
        var password = $form.find('input#password').val();
        window.auth = {
            'type': 'basic',
            'username': username,
            'password': password
        };
        $selectedAuthentication.text('basic');
        $authControl.find("[data-auth]").closest('li').removeClass('active');
        $authControl.find("[data-auth='basic']").closest('li').addClass('active');
        $authBasicModal.modal('hide')
    });

    // Authentication: session
    $('form.authentication-session-form').submit(function (event) {
        event.preventDefault();
        window.auth = {
            'type': 'session'
        };
        $selectedAuthentication.text('session');
        $authControl.find("[data-auth]").closest('li').removeClass('active');
        $authControl.find("[data-auth='session']").closest('li').addClass('active');
        $authSessionModal.modal('hide')
    })
});
